package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.mysql.jdbc.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lyj
 * @Title: AnnotationBeanFactory
 * @ProjectName lagou-transfer
 * @Description: TODO
 * @date 2020/6/7 10:53
 */

public class AnnotationBeanFactory {

    /**
     * 任务一：读取解析注解，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String,Object> map = new HashMap<>();  // 存储对象

    private static String packagePath=null;
    private static List<Class<?>> classes;



    /**
     * 思路：第一步扫描所有包，找出所有注解，然后给对应注解实例化对象
     */
    static {
        classes = new ArrayList<Class<?>>();
        try {
            PackageScanner("com.lagou.edu");
            System.out.println(classes);
            for (Class<?> aClass : classes) {
                //判断类上面是否有service注解
                if(aClass.isAnnotationPresent(Service.class)){
                        Service serviceAnnotation = aClass.getAnnotation(Service.class);
                        String value = serviceAnnotation.value(); //取注解中value的值

                    //为空则取首字母小写为名称
                    if(StringUtils.isNullOrEmpty(value)){
                         value = toLowerCaseFirstOne(aClass.getSimpleName());
                         System.out.println(value);
                    }
                        //实例化对象，并放到map集合中，主键是service注解里面的value的值
                        Object o = aClass.newInstance();
                        map.put(value,o);

                }


            }

            //解决依赖关系，也就是@Autowired注解的作用


            //遍历map集合，给对象中的属性注入
                for(String s:map.keySet()){
                    Object o = map.get(s);
                    Class<?> aClass = o.getClass();

                    //得到所有属性变量
                Field[] declaredFields = aClass.getDeclaredFields();
                for (int i = 0; i < declaredFields.length; i++) {
                    Field field = declaredFields[i];

                    //判断变量上是否有注解@Autowired
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
                        //取属性的名字，按名字注入
                        String fieldName = field.getName();
                        Object o1 = map.get(fieldName);
                        Method[] methods = aClass.getMethods();
                        //在map集合中取出父类的对象
                        Object newInstance = map.get(aClass.getAnnotation(Service.class).value());
                        for (int i1 = 0; i1 < methods.length; i1++) {
                            Method method = methods[i1];
                            if (method.getName().equalsIgnoreCase("set" + fieldName)) {  // 该方法就是 setAccountDao(AccountDao accountDao)
                                method.invoke(newInstance, o1);
                            }
                        }
                        //获取父对象的service注解的属性值，目的是从map集合中拿出原来创建的对象
                        Service serviceAnnotation = aClass.getAnnotation(Service.class);
                        String value = serviceAnnotation.value(); //取注解中value的值
                        //将注入属性后的父对象更新到map集合中
                        map.put(value, newInstance);


                    }
                }


            }
                //处理@Transaction注解
            for(String s:map.keySet()){
                Object object=map.get(s);
                Class<?> aClass =object.getClass();

                if (aClass.isAnnotationPresent(Transactional.class)) {
                    ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                    //根据是否实现了接口，来分别采用jdk或者cglib
                    Class<?>[] interfaces = aClass.getInterfaces();
                    Object proxyObject;
                    if ((interfaces.length>0)) {
                        proxyObject = proxyFactory.getJdkProxy(object);
                    }else {
                        proxyObject = proxyFactory.getCglibProxy(object);
                    }
                    map.put(s,proxyObject);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 任务二：对外提供获取实例对象的接口（根据id获取）
   public static  Object getBean(String id) {
        return map.get(id);
        }

    public static void main(String[] args) {

    }

    public static void PackageScanner(String basePackage) throws ClassNotFoundException {
        packagePath = System.getProperty("user.dir") + "\\src\\main\\java\\";
        String filePath = packagePath + basePackage.replace('.', '\\');

        fileScanner(new File(filePath));
    }


    private static void fileScanner(File file) throws ClassNotFoundException {
        if (file.isFile() && file.getName().endsWith(".java")) {//5是".java"的长度
            String filePath = file.getAbsolutePath();
            String qualifiedName = filePath.substring(packagePath.length(), filePath.length() - 5).replace('\\', '.');
            System.out.println(qualifiedName);
            if(!qualifiedName.contains("servlet")
                &&!Class.forName(qualifiedName).isInterface()
                &&!Class.forName(qualifiedName).isAnnotation()
                    ){
                classes.add(Class.forName(qualifiedName));
            }
            return;
        } else if (file.isDirectory()) {
            for (File f : file.listFiles())
                fileScanner(f);
        }
    }

    //首字母转小写
    public static String toLowerCaseFirstOne(String s){
        if(Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }


}
